iT邦幫忙

2026 iThome 鐵人賽

DAY 15
0
AI 自動化

情報進來,內容發出去:30 天拆一條每天真的在跑的 AI 內容產線系列 第 15

Day 15|從逐字稿包到可審會議報告,整條流程真的接起來了嗎?

  • 分享至 

  • xImage
  •  

一份會議逐字稿進來,最後要變成一份可以交給人審的會議報告。中間要過五關:每句話回得到原始字幕、分得出誰跟誰在說話、決定要不要把匿名標記換成真名、挑出重點、把「有人答應要做的事」變成一筆待辦。

這五關,我過去五天一關一關做完,每關都有自己的測試,每關都綠燈。

但這個系列做到現在,我從來沒有讓同一份逐字稿,真的從頭走到尾一次。

零件會過,不代表接得起來。中間那些交接,每一處都可能默默把東西弄丟,而且不留紀錄。

今天就是驗這件事。結論先講:四條路徑跑完,三條走到「等人審的報告」就停住,一條在中途該停的地方停住。沒有任何東西被發布出去。

這篇要做到的,就這一句:同一份來源,走到底都還認得出是同一份

為什麼零件全綠還不夠

Google 那篇很有名的測試文章把測試分層:小型測試、整合測試、端到端測試,責任不一樣。Microsoft 講得更直接,整合測試是在看兩個以上的元件「一起」產生的結果對不對,不是重跑每個函式自己的測試。Cypress 的文件也把元件測試(隔離掛載單一元件)跟端到端測試(跨越各層看協作)分開。

共同點只有一句:只知道每個零件會動,回答不了「交接有沒有成立」

那接縫會怎麼壞?舉個最容易發生的:A 模組傳給 B 模組,欄位名字剛好都叫 segment_id,看起來很合,直接接上去。但 A 那邊還有說話者標記,B 不需要就沒帶過去。三站之後有人要問「這句是誰說的」,答不出來,而且沒有任何紀錄說明它是在哪裡被丟掉的。

這次的「端到端」,範圍到哪裡

先把邊界畫清楚,免得標題騙人。

起點:一份已知內容、帶時間碼跟匿名說話者標記的合成逐字稿包。不是真實錄音,不是語音辨識,不是我的私人會議。

終點:一份還在等人審、沒有任何發布能力的會議報告。

中間不測任何模型的摘要品質或抽取品質。那些輸入全部是預先錄好的固定測試資料。

所以這篇的「端到端」指的是這段本機控制流程的頭尾,不是從麥克風一路到正式系統。

接法不是一條直線。實際長這樣:

                      ↗ 回放證據
                      ├→ 匿名發言區間(只在測試層核對,執行時不跑)
  根來源 ─────────────┼→ 身分分支 ───────────┐
                      ├→ 摘要 ───────────────┼→ 等人審的報告
                      └→ 行動來源 → 建立待辦 ┘

一份根來源在最上面,往下同時餵給五個地方。每個地方只拿它需要的那幾個欄位,就像同一份資料照五種不同的模板各印一份。

這五條裡,只有「行動來源」那一條會再往下走,接到第 14 天做的建立待辦。

最後,摘要、身分、行動三邊的狀態,匯合成一份報告。

前五天各留下什麼,今天怎麼消費

天數 那天驗到的 今天怎麼用
10 時間碼能落回字幕片段跟公開播放位置 先驗根來源與回放證據
11 匿名分群跟身分綁定是兩種判定 用測試專用的標準答案,核對匿名發言區間
12 匿名報告先完成,身分分支可以等或拒絕 三種身分決定分開重播
13 固定提案經過三層驗證變成候選 從目前根來源重新投影、重算來源綁定
14 批准後原樣重送,假服務只建立一次 每條沒過期的路徑各自重送兩次

有件事要老實說:第 10 天當時的公開證據快照沒有逐字全文,第 11 到 13 天的固定測試資料也不是同一份來源。所以這張表是「能力接力」,不是「既有整線紀錄」。今天是第一次讓它們吃同一份根來源。

怎麼證明兩次跑的是同一份東西

這裡要先講一個詞。指紋,指的是把一份內容算成一串固定長度的字:內容一改,這串字就完全不一樣。它不能還原內容,只能回答「這兩份是不是同一份」。下面說的「根指紋」「文字指紋」都是這個意思,跟會議摘要沒有關係。

要驗「同一份來源」,得先讓「同一份」可以被重算。

所以根來源不是只存一個檔名。它把這些東西放進同一份清單:字幕原始位元組的指紋、回放帳本的指紋、版本號,以及每一段的識別碼、順序、起訖毫秒、匿名說話者、文字、文字指紋。

這樣「同一份」就有了可檢查的定義:四條路徑的第 1 版必須算出同一個根指紋。

過期那條路徑也因此比較有力。我不是只把版本號從 1 改成 2 就算換版。那太便宜了,內容沒動,什麼都證明不了。實際做法是同時改掉某一段保留的時間範圍文字,讓根指紋跟所有下游的副本都跟著變。

每條接縫,都要寫清楚保留什麼、丟掉什麼

五條交接線(我在文章裡叫「資料邊」),每一條都要交代它保留了什麼、丟掉了什麼:

交接線 保留 明示丟掉
回放證據 片段識別碼、字幕提示序號、起訖時間、文字指紋 說話者標記、原始文字
匿名發言區間 片段識別碼、起訖時間、說話者標記 字幕提示序號、原始文字、文字指紋
等人確認身分的請求 片段識別碼、起訖時間(說話者標記改名成另一個欄位) 字幕提示序號、原始文字、文字指紋
行動來源 片段識別碼、起訖時間、說話者標記、原始文字 字幕提示序號、根來源裡的文字指紋
摘要封套(把選中的重點連同它們的出處包成一份) 只有被選中片段的識別碼、起訖時間、文字指紋 所有片段的序號、說話者、原始文字;沒被選中的片段連識別碼跟時間都沒了

看第五條。摘要封套丟掉的東西最多,這很正常,摘要本來就是取捨。問題不在丟,在有沒有寫下來

丟掉不申報,就是前面講的那個接縫:欄位名字看起來很像,直接傳過去,資訊在中間消失,而且沒人知道是在哪一站消失的。

所以每條交接線都寫成可以重算的紀錄:吃進去哪份產物(連指紋一起記)、吐出什麼產物(連指紋一起記)、用哪一版的轉換程式(負責把根來源改寫成下游要的格式)、欄位怎麼對應、少了哪些欄位。

兩種紀錄,各自回答什麼

這兩個很容易混在一起,但它們回答的問題不一樣:

執行軌跡回答:這一輪走過哪些步驟、在哪裡停。

資料譜系回答:這份產物實際吃了哪份輸入、經過哪一版轉換、產出哪個指紋。

OpenTelemetry 把 trace 看成請求經過的路徑,span 是其中一個工作單元,靠脈絡傳遞把散在各處的 span 關聯起來。W3C 的 Trace Context 則定義了跨系統傳這個識別碼的共同格式。

但這兩份規格解的都是「這些步驟屬於哪一次執行」。它們不會告訴你某個摘要忠不忠於逐字稿,也不會告訴你輸出是從哪一版輸入轉來的。

OpenLineage 的文件講了一個很關鍵的提醒:輸入跟輸出同時出現在一個事件裡,不代表每個輸入都流向每個輸出

這就是為什麼我不用「把所有檔案列在一張清單裡」交差。只有一個執行編號、或只寫個「衍生自」字樣,最多能把紀錄關聯起來,證明不了內容真的接對。每條交接線要個別記「吃進去的指紋」跟「吐出來的指紋」,才分得出哪份資料真的進了哪個模組。

(這些資料形狀是被上面幾份規格啟發的,但沒有宣稱相容 OpenTelemetry、OpenLineage 或 W3C PROV。)

測試的答案,不能餵給流程

同一輪測試裡有兩份長得很像的資料:一份是執行時可以看到的身分候選證據;另一份是只給測試層用的標準答案,用來核對匿名標記有沒有映射對。

如果標準答案滲進執行產物,流程看起來像是把某個匿名標記對到了某個人,實際上只是把答案從測試檔抄過去而已。這種「假通過」自己不會報錯。

做法是把標準答案鎖在最上層的測試專用區,執行器、情境結果、報告、執行軌跡、資料譜系全部不准讀它,也不准輸出它衍生的摘要。執行期那個元件的狀態就直接寫成「沒跑,只是測試層的前置條件」,軌跡事件也標「沒跑」加上原因碼。

然後有一條專項測試,直接去搜尋序列化後的結果,確認測試答案沒有滲進任何執行產物。

四條路徑,跑出來長這樣

表裡的「假服務」是一個跑在本機、沒有任何網路呼叫的假待辦服務,可以反覆弄壞。

路徑 身分結果 報告 行動支線 服務收到/真的建立/最後有幾筆
正常 確認身分 保留,等人審 第一次真的建了一筆,第二次服務認出是同一件事、把第一次的結果還回來 2/1/1
維持匿名 只有匿名中繼資料 保留,等人審 跟正常路徑同一份批准 2/1/1
拒絕候選 具名支線停止 匿名報告仍保留 身分拒絕不撤銷已獨立批准的行動 2/1/1
來源過期 舊複核標記過期、重發請求 不產生舊版報告 第 13 天停止、第 14 天沒被呼叫 0/0/0

前三條路徑共用完全一樣的根來源、摘要候選、行動提案、候選、請求跟行動批准,只換身分決定這一個變數。三份報告最後都停在「等人審」,發布能力都是空的。

第三條特別值得看:身分被拒絕了,但行動那條支線不受影響。因為那是另一份獨立的批准,不該被身分決定連坐。同樣的,身分確認了也不會讓報告自動變成已批准。

過期那條:整包都舊,內部卻自洽

這是我覺得最有價值的一條。

假設候選、請求、批准三份資料互相對得上,摘要全部吻合。第 14 天的執行器會怎麼想?它會覺得沒問題,因為它只驗這三份彼此一致。它不知道整包都是舊的,根來源早就換版了。

所以在真正產生副作用之前,多加一次頂層核對:把目前的根來源重新投影成行動來源,再跟舊候選的來源參照比對。

換成第 2 版之後,保存的結果同時出現這些:第 12 天把等人確認身分的舊請求標成過期並重發、第 13 天以「來源過期」停止、摘要封套也因為舊來源停下來、頂層閘門根本不呼叫第 14 天的執行器、假服務維持 0/0/0、沒有產生舊版報告。

整條路徑的狀態是「等待重新驗證」,停點寫得很清楚。

這個模式不是我發明的。RFC 9110 的 If-Match 就是「目前版本跟你給的標籤不符時,不准執行狀態變更」。Kubernetes 也一樣:更新時要帶著你讀取當下的版本號,版本過期就用衝突拒絕,避免舊讀取覆蓋現況。

我借的只是「副作用前重新核對目前版本」這個模式。根摘要怎麼算、在哪裡停、回什麼原因碼,都是這個專案自己要負責驗的。

故意弄壞六種接縫

一條流程接沒接起來,不是看它順跑一次,是看假的整合會不會被抓出來。所以要故意壞六種:

1/ 字幕的時間範圍還是合法的,但提示識別碼或文字被換掉。

2/ 譜系裡「吃了哪份產物」的摘要,指向另一份產物。

3/ 身分複核那條邊省略了文字,卻把損失聲明刪掉。

4/ 把測試專用的標準答案塞進執行產物。

5/ 拿身分決定去冒充行動批准,或讓行動批准去改報告狀態。

6/ 候選、請求、批准三份互相一致,但全都綁在舊的根來源上。

這六項分別對應六種「看起來接起來了、其實沒有」:回放只看範圍、譜系只寫關係字樣、資訊丟了沒申報、測試答案污染、權限混用、整包過期。

第 15 天的測試檔這次跑 20 條全過,含四條路徑、五條資料邊的重算、這六種接縫,以及保存的結果能被程式完整重算。

四種批准,四把鑰匙

決定或批准 能改什麼 不能做什麼
身分決定 產生具名或維持匿名的說話者中繼資料 不能建待辦、批准報告、發布
行動批准 只准對指定的候選、來源、請求做「建立一筆假待辦」這一件事 不能改身分,也不能改報告狀態
報告審閱 這篇只停在等待 沒有實作批准介面
發布能力 固定是空的 不能對外發布

這個原則在別的地方也看得到。Azure Pipelines 讓資源擁有者替受保護資源設定批准與檢查,一個階段用到多個受保護資源時,各自的檢查都要成立。GitHub 的 environments 可以要求指定審查者批准才繼續,而且環境祕密在規則通過前不會給工作。

共同的形狀是:批准綁在它要動的那個資源或階段上,不是一個全域的 approved: true

今天的驗法是拿第 12 天的身分收據去跑第 14 天的驗證器,確認它被拒絕;再確認合法的行動批准跑完之後,報告還是「等人審」。

這次證明了什麼,沒證明什麼

證明了:

1/ 同一份根來源,能沿五條資料邊投影出去,每條邊的保留與損失都可以重算。

2/ 三條路徑只換身分決定這一個變數,其他輸入與行動批准完全相同,都停在「等人審」。

3/ 根來源換版時,複核重發、候選停止、外部副作用維持 0/0/0,不產生舊版報告。

4/ 六種假接通都會被抓到,包括測試答案污染跟權限混用。

沒證明的:

▸ 真實錄音、語音辨識、模型摘要品質。起點就是合成逐字稿包,摘要跟提案都是預錄的固定資料。

▸ 真實待辦服務。仍然是本機假服務,沒有網路呼叫。

▸ 並行、當機恢復、恰好一次。單寫入者、序列重送,每條路徑各自隔離狀態。

▸ 相容任何外部標準。譜系跟摘要的序列化都是專案內契約。

▸ 我的私人會議流程。這是教學用的合成切片。

下一篇:換案例,但不重學一次

第 15 天交出的,其實不是這條會議切片本身,是一張可以重用的責任圖:

輸入先驗、產物綁版本、分支各有各的權限、外部副作用之前重查目前來源、每個停止點都留下可回查的狀態。

第 16 天會把案例換回主線的「情報進來、內容發出去」,第一個問題是:定時執行的時候,它怎麼判斷這一輪該處理什麼?

它只繼承這張責任圖跟停點設計,不會拿會議報告當輸入。要到第 30 天,才把內容產線累積的契約完整回套到這條會議切片。

所以換線不是跳題。是帶著已經驗收過的方法,去學下一個新的自動化能力。

參考資料


上一篇
Day 14|行動批准後建立待辦,重跑為什麼不能建立兩次?
下一篇
Day 16|排程啟動後,這一輪要處理哪些新資料?
系列文
情報進來,內容發出去:30 天拆一條每天真的在跑的 AI 內容產線18
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言